/**
 * Copyright 2010 The Regents of the University of California
 *
 *  All Rights Reserved
 */

#include <config.h>

#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include <inttypes.h>
#include <netinet/in.h>

#include "dcswitch.h"
#include "flow.h"
#include "learning-switch.h"
#include "new-switch-handler.h"
#include "flow-insertion.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "poll-loop.h"
#include "queue.h"
#include "rconn.h"
#include "timeval.h"
#include "vconn.h"
#include "xtoxll.h"
#include "arp-handling.h"
#include "fault-manager.h"

#define THIS_MODULE VLM_flow_insertion
#include "vlog.h"

#define HOST_ID(x) ((((x) & 0x00FF0000)>>16) * 4 + (((x) & 0x0000FF00) >> 8) * 2 + ((x) & 0x000000FF) - 2)

//////////////////////////////////////////////////////////
// static function declarations
//////////////////////////////////////////////////////////


bool create_upward_flow_entry(struct rconn* rconn, struct lswitch *sw, struct flow* flow, struct ofp_packet_in* opi);
void init_switch_prefixes(int sw_index, struct lswitch* sw, struct rconn* rconn);
uint32_t convert_to_uint_ip(uint8_t *dstMAC);



void insert_modify_L2_entry(struct rconn* rconn, struct lswitch* sw, uint8_t* dstAMAC, uint8_t *dstPMAC, uint16_t port);
void insert_L2_entry(struct rconn* rconn, struct lswitch* sw, uint8_t *dstPMAC, uint16_t port);
void insert_modify_L3_entry(struct rconn* rconn, struct lswitch* sw, uint32_t dstAIP, uint32_t dstPIP , uint16_t port);
void insert_L3_entry(struct rconn* rconn, struct lswitch* sw, uint32_t dstPIP, uint16_t port);
struct ofpbuf* build_simple_flow_entry(struct flow *flow,
                         uint32_t buffer_id);

//////////////////////////////////////////////////////////
// Function definitions
//////////////////////////////////////////////////////////


/**
 * This function inserts a new flow entry based on the logic
 * originally implemented in Portland
 *
 * The new flow entry is an exact-match entry and should be inserted into SRAM.
 *
 * @param 
 *
 */
bool create_upward_flow_entry(struct rconn* rconn, struct lswitch *sw, struct flow* flow, struct ofp_packet_in* opi )
{
	int out_index=OFPP_FLOOD;
	uint32_t dst_aip; 
	arp_cache_record *acr;
	location loc;
	int fattree_index;
	DCSwitch_node *node;
	int sw_index = new_lookup(sw->datapath_id);
	int index = new_lookup(sw->datapath_id);
	node = DCSwitches[index];
	fattree_index = node->fattree_index;
	dst_aip = ntohl(flow->nw_dst);


	compute_shortest_path(index);
 
	VLOG_DBG("Recieved flow in create_entry in switch: %d %d",fattree_index, sw_index);

	if(fattree_index >=K*K)
	{
		if(getInsertDownwardFlowEntries())
		{
			VLOG_WARN("PANIC!! Core switch sending packet_in");
			return true;
		}
		else
		{
			if(getFlowEntryMatching() == PMAC)
			#ifdef VM
				out_index = determine_output_port(index, (flow->dl_dst)[2],(flow->dl_dst)[3], (flow->dl_dst)[4]);
			#else		
				out_index = determine_output_port(index, (flow->dl_dst)[3],(flow->dl_dst)[4], (flow->dl_dst)[5]);
			#endif
			else if(getFlowEntryMatching() == PIP)
			{
				VLOG_DBG("..................Network dest : %.8x", dst_aip);
				loc = extractLocationFromIP(dst_aip);
				VLOG_DBG("pod = %d, position = %d, port = %d", loc.pod, loc.position, loc.port);
				assert(loc.pod < K && loc.position < K && loc.port < K);	

				out_index = determine_output_port(index,loc.pod, loc.position, loc.port); 
			}

			VLOG_DBG("OUTPUT PORT : %d", out_index);

			if(getFlowEntryMatching() == PMAC)
				insert_L2_entry(rconn, sw, flow->dl_dst, out_index);
			else if(getFlowEntryMatching() == PIP)
				insert_L3_entry(rconn, sw, dst_aip, out_index);
			return true;
		}
	}
	else
	{
		if(fattree_index % K < K/2)
		{

			//VLOG_WARN("Out_port for %x to %x on switch %d %d %d: %d. Aggr_index: %d",ntohl(flow->nw_src),ntohl(flow->nw_dst),  fattree_index, sw_index, index, node->ports[out_index][0], (node->neighbours[out_index])->sw_indexTmp);
				
			if(getFlowEntryMatching() == PMAC)
			{
				VLOG_DBG("Flow table entry -- MODIFIED!*******");
				#ifdef VM
					out_index = determine_output_port(index, (flow->dl_dst)[2],(flow->dl_dst)[3], (flow->dl_dst)[4]);
				#else
					out_index = determine_output_port(index, (flow->dl_dst)[3],(flow->dl_dst)[4], (flow->dl_dst)[5]);
				#endif


				VLOG_DBG("OUTPUT PORT : %d", out_index);
				insert_L2_entry(rconn, sw, flow->dl_dst, out_index);
			}
			else if(getFlowEntryMatching() == PIP)	
			{
				acr = arp_cache_entry(&arp_cache_table, dst_aip);
				if(acr!=NULL)
				{
					location loc = extractLocationFromIP(acr->pip);
					VLOG_DBG("pod = %d, position = %d, port = %d", loc.pod, loc.position, loc.port);
					assert(loc.pod < K && loc.position < K && loc.port < K);	
					out_index = determine_output_port(index, loc.pod, loc.position, loc.port);
					insert_modify_L3_entry(rconn, sw, dst_aip, acr->pip, out_index);
				}
				else
					VLOG_WARN("Dest host aip %x is not present in arp-cache", dst_aip);
			}
		}
		else
		{
			VLOG_DBG("Inserting flow across pods !!!!!!!!");
			
			if(getFlowEntryMatching() == PMAC)
			{
				VLOG_DBG("Flow table entry -- MODIFIED!*******");
				#ifdef VM 
				out_index = determine_output_port(index, (flow->dl_dst)[2],(flow->dl_dst)[3], (flow->dl_dst)[4]);
				#else
				out_index = determine_output_port(index, (flow->dl_dst)[3],(flow->dl_dst)[4], (flow->dl_dst)[5]);
				#endif
				VLOG_DBG("OUTPUT PORT : %d", out_index);
				insert_L2_entry(rconn, sw, flow->dl_dst, out_index);
			}
			else if (getFlowEntryMatching() == PIP)
			{
				location loc = extractLocationFromIP(dst_aip);
				VLOG_DBG("pod = %d, position = %d, port = %d", loc.pod, loc.position, loc.port);
				assert(loc.pod < K && loc.position < K && loc.port < K);	

				out_index = determine_output_port(index, loc.pod, loc.position, loc.port);

				insert_L3_entry(rconn, sw, dst_aip, out_index);
			}
		}
		return true;
	}
}
/*
static void print_entry(uint8_t *mac, int out_port)
{
	printf("%x:%x:%x:%x\t%d\n", mac[2], mac[3], mac[4], mac[5], out_port);
}	
*/
void insert_L2_entry(struct rconn* rconn, struct lswitch* sw, uint8_t *dstPMAC, uint16_t port)
{
	struct ofp_flow_mod *ofm;
	struct ofp_action_output *oao;
	ssize_t size;
	uint32_t wildcard;
	struct ofpbuf *out;
	VLOG_DBG("[%s] started", __PRETTY_FUNCTION__);

	wildcard = OFPFW_IN_PORT |
					OFPFW_DL_SRC |
					OFPFW_NW_PROTO | 
					OFPFW_TP_SRC | 
					OFPFW_TP_DST |
					OFPFW_DL_VLAN |
					OFPFW_NW_SRC_ALL |
					OFPFW_NW_DST_ALL ;


    size = sizeof *ofm + sizeof *oao;
    out = ofpbuf_new(size);
    ofm = ofpbuf_put_zeros(out, size);

    ofm->header.version = OFP_VERSION;
    ofm->header.type = OFPT_FLOW_MOD;
    ofm->header.length = htons(size);
	
    ofm->match.dl_type = htons(0x0800);
    ofm->match.wildcards = htonl(wildcard);

    memcpy(ofm->match.dl_dst, dstPMAC, 6);

    
    ofm->command = htons(OFPFC_ADD);
    ofm->idle_timeout = htons(getIdleTimeout());
    ofm->hard_timeout = htons(getHardTimeout());
    ofm->buffer_id = htonl(-1);

    oao = (struct ofp_action_output *)&ofm->actions[0];
    oao->type = htons(OFPAT_OUTPUT);
    oao->len = htons(sizeof *oao);
    oao->port = htons(port);
	
    queue_tx(sw, rconn, out);
	VLOG_DBG("[%s] ended", __PRETTY_FUNCTION__);

}

void modify_L2_entry(struct rconn* rconn, struct lswitch* sw, uint8_t *dstPMAC, uint16_t port, uint16_t orig_output_port, const struct ofp_flow_stats *fs)
{
	struct ofp_flow_mod *ofm;
	ssize_t size;
	uint32_t wildcard;
	struct ofpbuf *out;

	VLOG_WARN("Modifying forwarding prefix in the switch-----------------------------------\n");


    wildcard = OFPFW_IN_PORT |
                OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                OFPFW_DL_DST |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_NW_SRC_ALL |
                OFPFW_NW_DST_ALL |
                OFPFW_TP_SRC |
                OFPFW_TP_DST ;                   
	size = sizeof *ofm;
   	out = ofpbuf_new(size);
  	ofm = ofpbuf_put_zeros(out, size);

   	ofm->header.version = OFP_VERSION;
   	ofm->header.type = OFPT_FLOW_MOD;
   	ofm->header.length = htons(size);

   	ofm->match.dl_type = htons(0x0800);
   	ofm->match.wildcards = htonl(OFPFW_ALL);

   	ofm->command = OFPFC_DELETE;
   	ofm->out_port = OFPP_NONE;

   	queue_tx(sw, rconn, out);
}

uint32_t convert_to_uint_ip(uint8_t *dstMAC)
{
		uint32_t ip_add;
		ip_add = 0x0a << 24;
		#ifdef VM
		VLOG_DBG("Supporting vm migration\n");
		ip_add += ((dstMAC[5] & 15) | (dstMAC[4]<<4)) + (dstMAC[3] << 8) + (dstMAC[2] <<16);
		#else
		ip_add += dstMAC[5] + (dstMAC[4] << 8) + (dstMAC[3] << 16);
		#endif
	
		//ip_add = dstMAC[5] + (dstMAC[4] << 8) + (dstMAC[3] << 16) + (dstMAC[2] << 24);
		VLOG_DBG("Dst PIP: %x",ip_add);
		return ip_add;
}	

location extractLocationFromIP(uint32_t pip)
{
	location loc;
	loc.pod = 0;
	loc.position = 0;
	loc.port = 0;
	loc.vmid = 0;
		
	#ifdef VM
	loc.pod = (pip & 0x00ff0000) >> 16;
	loc.position = (pip & 0x0000ff00) >> 8;
	loc.port = (pip & 0x000000f0)>>4;
	loc.vmid = (pip &  0x0000000f);

	#else
	loc.pod = (pip & 0x00ff0000) >> 16;
	loc.position = (pip & 0x0000ff00) >> 8;
	loc.port = pip & 0x000000ff;
	loc.vmid = 0;
	#endif

	VLOG_DBG("IP: %x pod:%d position:%d port:%d vmid:%d", pip, loc.pod, loc.position, loc.port, loc.vmid); 
	
	return loc;
	
}

void delete_L2_entry(struct rconn* rconn, struct lswitch* sw)
{
    struct ofp_flow_mod *ofm;
    ssize_t size;
    uint32_t wildcard;
    struct ofpbuf *out;

    printf("Deleting forwarding prefix in the switch\n");


    wildcard = OFPFW_IN_PORT |
                OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                OFPFW_DL_DST |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST ;                    

    size = sizeof *ofm;
    out = ofpbuf_new(size);
    ofm = ofpbuf_put_zeros(out, size);

    ofm->header.version = OFP_VERSION;
    ofm->header.type = OFPT_FLOW_MOD;
    ofm->header.length = htons(size);

    ofm->match.dl_type = htons(0x0800);
    ofm->match.wildcards = htonl(wildcard);

    ofm->command = htons(OFPFC_DELETE);
    ofm->buffer_id = htonl(-1);
    ofm->out_port = OFPP_NONE;

    queue_tx(sw, rconn, out);

}

void send_flow_stats_request(struct rconn* rconn, struct lswitch* sw, uint16_t port_no) 
{
    struct ofp_stats_request *osr;
    struct ofp_flow_stats_request *ofsr;
    size_t size = sizeof *osr + sizeof *ofsr;
    struct ofpbuf *out;

    //32 bit random #
    uint32_t xid = random_uint32();
    
    osr =  make_openflow_xid(size,
                             OFPT_STATS_REQUEST,
                             xid,
                             &out);
                             
    osr->type = htons(OFPST_FLOW);
    osr->flags = htons(0);

    ofsr = (struct ofp_flow_stats_request*) osr->body;
    ofsr->match.wildcards = htonl(OFPFW_ALL);
    ofsr->table_id = 0xff;
    ofsr->out_port = htons(port_no);

    // track that this flow stats request originated from secchan
    
    VLOG_WARN("[%s] - Flow Stats Request xid = %u submitted from secchan\n",
              __PRETTY_FUNCTION__,
             xid);
    // enqueue for processing by main event loop
    queue_tx(sw, rconn, out);
}

void match_dl_dst(struct rconn* rconn, struct lswitch* sw, uint8_t *dl_dst) 
{
    struct ofp_stats_request *osr;
    struct ofp_flow_stats_request *ofsr;
    size_t size = sizeof *osr + sizeof *ofsr;
    struct ofpbuf *out;
        uint32_t wildcard;

    //32 bit random #
    uint32_t xid = 1000;

        wildcard = OFPFW_IN_PORT |
                OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST ;                           // CHECK FOR OFPFW_ALL
    
    //osr =  make_openflow_xid(size,
    //                         OFPT_STATS_REQUEST,
    //                         xid,
    //                         &out);
    out = ofpbuf_new(size);
    osr = ofpbuf_put_zeros(out, size);
                             
    osr->type = htons(OFPST_FLOW);
    osr->flags = htons(0);

    osr->header.version = OFP_VERSION;
    osr->header.type = OFPT_STATS_REQUEST;
    osr->header.length = htons(size);
    osr->header.xid = xid;

    ofsr = (struct ofp_flow_stats_request*) osr->body;
    memcpy(ofsr->match.dl_dst, dl_dst, 6);
    ofsr->match.wildcards = htonl(wildcard);
    ofsr->table_id = 0xff;
    ofsr->out_port = htons(OFPP_NONE);

    // track that this flow stats request originated from secchan
    
    VLOG_DBG("[%s] - Flow Stats Request xid = %u submitted from secchan\n",
              __PRETTY_FUNCTION__,
             xid);
    // enqueue for processing by main event loop
    queue_tx(sw, rconn, out);
}

void match_nw_dst(struct rconn* rconn, struct lswitch* sw, uint32_t nw_dst) 
{
    struct ofp_stats_request *osr;
    struct ofp_flow_stats_request *ofsr;
    size_t size = sizeof *osr + sizeof *ofsr;
    struct ofpbuf *out;
    uint32_t wildcard;

    uint32_t xid = 1000;

        wildcard = OFPFW_IN_PORT |
                OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                OFPFW_DL_DST |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST ;                          
    
    out = ofpbuf_new(size);
    osr = ofpbuf_put_zeros(out, size);
                             
    osr->type = htons(OFPST_FLOW);
    osr->flags = htons(0);

    osr->header.version = OFP_VERSION;
    osr->header.type = OFPT_STATS_REQUEST;
    osr->header.length = htons(size);
    osr->header.xid = xid;

    ofsr = (struct ofp_flow_stats_request*) osr->body;
    //memcpy(ofsr->match.dl_dst, dl_dst, 6);
    ofsr->match.nw_dst = nw_dst; 
    ofsr->match.wildcards = htonl(wildcard);
    ofsr->table_id = 0xff;
    ofsr->out_port = htons(OFPP_NONE);

    // track that this flow stats request originated from secchan
    
    VLOG_DBG("[%s] - Flow Stats Request xid = %u submitted from secchan\n",
              __PRETTY_FUNCTION__,
             xid);
    // enqueue for processing by main event loop
    queue_tx(sw, rconn, out);
}

void process_flow(struct lswitch *sw, struct rconn *local, const struct ofp_flow_stats *fs)
{
    int i;
	DCSwitch_node* node;
    uint8_t dl_dst[OFP_ETH_ALEN];
	uint32_t nw_dst;
	uint16_t out_ports[K];
    struct ofp_match *om = &(fs->match);
    for (i = 0; i < K; i++)
    {
         out_ports[i] = OFPP_NONE;
    }

	//We need to determine the DL_DST that going going to be affected because of this fault.
	// On the basis of this information, we will determine DL_SRC

	if(getFlowEntryMatching() == PMAC)
	{
		memcpy(dl_dst, om->dl_dst, OFP_ETH_ALEN);	
		VLOG_DBG("[Switch %d] Affected DL_DST -> %x:%x:%x:%x:%x:%x ",new_lookup(sw->datapath_id),dl_dst[0], dl_dst[1], dl_dst[2], dl_dst[3], dl_dst[4], dl_dst[5]);
	}
	else if(getFlowEntryMatching() == PIP)
	{
		nw_dst = om->nw_dst;
		VLOG_WARN("Affected PIP = %.8x", nw_dst );
	}

	node = DCSwitches[new_lookup(sw->datapath_id)];
	if(node->level == AGGR || node->level == CORE)
	{
		// In case that one of the faulty node is AGGR, the delete the flow entry so that either of the following 2 cases happen
		// 1. A new path is choosen from the EDGE
		// 2. If the same path is choosen from the EGDE, then the path from the AGGR is recalculated using compute_shortest_path
		VLOG_WARN("DELETING FLOW ENTRIES FOR AGGR");
		if(getFlowEntryMatching() == PMAC)
			deleteFlowEntries(node->rconn, node->sw,om->dl_dst); 	
		else if(getFlowEntryMatching() == PIP)
			deleteFlowEntriesPIP(node->rconn, node->sw,om->nw_dst); 	
		
	}

	for(i=0; i<MAX_SWITCHES; i++)
	{
		//Now that we have the affected DL_DST, we need to determine the DL_SRC, 
		// for this reason, we are going to poll all the edge swithes if they have a flow entry
		node = DCSwitches[i];	
		if(node != NULL && node->level == EDGE)
			match_dl_dst(node->rconn, node->sw,  om->dl_dst);
		else if(getFlowEntryMatching() == PIP)
			match_nw_dst(node->rconn, node->sw, om->nw_dst);
	}

}

void fill_flow (struct flow *flow, struct ofp_match match)
{
    flow->in_port = match.in_port;

    memcpy(flow->dl_src, match.dl_src, ETH_ADDR_LEN);
    memcpy(flow->dl_dst, match.dl_dst, ETH_ADDR_LEN);
    flow->dl_vlan = match.dl_vlan;
    flow->dl_type = match.dl_type;
    flow->nw_src = match.nw_src;
    flow->nw_dst = match.nw_dst;
    flow->nw_proto = match.nw_proto;
    flow->tp_src = match.tp_src;
    flow->tp_dst = match.tp_dst;
}

struct ofpbuf* build_simple_flow_entry(struct flow *flow,
                         uint32_t buffer_id)
{
    struct ofpbuf *buffer = make_add_simple_flow(flow, buffer_id, 0, 300);
    struct ofp_flow_mod *ofm = buffer->data;
    VLOG_DBG("[%s] started\n",__PRETTY_FUNCTION__);
    ofm->command = htons(OFPFC_MODIFY);
    ofm->out_port = OFPP_NONE;
    VLOG_DBG("[%s] ended\n",__PRETTY_FUNCTION__);
    return buffer;
}

void set_output_port(struct ofpbuf *buffer, uint16_t out_port)
{
    struct ofp_flow_mod *ofm = buffer->data;
    struct ofp_action_output *oao;
    struct ofp_action_header *oah = (struct ofp_action_header *)&ofm->actions[0];

    assert(ntohs(oah->type) == OFPAT_OUTPUT);
    assert(ntohs(oah->len) == sizeof *oao);

    oao = (struct ofp_action_output *)oah;
    oao->port = htons(out_port);
}

void modifyFlowEntry(struct rconn* rconn, struct lswitch* sw, uint8_t *dstPMAC, uint16_t port)
{
	struct ofp_flow_mod *ofm;
	struct ofp_action_output *oao;
	ssize_t size;
	uint32_t wildcard;
	struct ofpbuf *out;

	VLOG_WARN("DELETING forwarding prefix in the switch\n");
	VLOG_DBG("%x:%x:%x:%x:%x:%x PORT:%d", dstPMAC[0],dstPMAC[1],dstPMAC[2],dstPMAC[3],dstPMAC[4],dstPMAC[5],port);


    wildcard = OFPFW_IN_PORT |
               OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                //OFPFW_DL_DST |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST ;                         
	size = sizeof *ofm + sizeof *oao;
   	out = ofpbuf_new(size);
   	ofm = ofpbuf_put_zeros(out, size);

   	ofm->header.version = OFP_VERSION;
   	ofm->header.type = OFPT_FLOW_MOD;
   	ofm->header.length = htons(size);

   	ofm->match.dl_type = htons(0x0800);
   	ofm->match.wildcards = htonl(wildcard);
   	memcpy(ofm->match.dl_dst, dstPMAC, 6);

   	ofm->command = htons(OFPFC_MODIFY);
   	ofm->buffer_id = htonl(-1);
   	ofm->out_port = OFPP_NONE;

   	oao = (struct ofp_action_output *)&ofm->actions[0];
   	oao->type = htons(OFPAT_OUTPUT);
   	oao->len = htons(sizeof *oao);
	//size = sizeof *ofm;
   	oao->port = htons(port);

   	queue_tx(sw, rconn, out);
}

void modifyFlowEntryPIPEdge(struct rconn* rconn, struct lswitch* sw, uint32_t AIP, uint32_t PIP, uint16_t port)
{
	struct ofp_flow_mod *ofm;
	struct ofp_action_output *oao;
  	struct ofp_action_nw_addr *oana;
	ssize_t size;
	uint32_t wildcard;
	struct ofpbuf *out;

   	VLOG_DBG("Inserting modify L3 entry in the edge switch AIP: %x PIP: %x port: %d\n", AIP, PIP, port);

    wildcard = OFPFW_IN_PORT |
                OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                OFPFW_DL_DST |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST;

	size = sizeof *ofm + sizeof *oao + sizeof *oana;
   	out = ofpbuf_new(size);
   	ofm = ofpbuf_put_zeros(out, size);

   	ofm->header.version = OFP_VERSION;
   	ofm->header.type = OFPT_FLOW_MOD;
   	ofm->header.length = htons(size);

   	ofm->match.dl_type = htons(0x0800);
    ofm->match.nw_dst = (AIP);


  	ofm->command = htons(OFPFC_MODIFY);
   	ofm->buffer_id = htonl(-1);
   	ofm->out_port = OFPP_NONE;

   	oana = (struct ofp_action_nw_addr *)&ofm->actions[0];
   	oana->type = htons(OFPAT_SET_NW_DST);
   	oana->len = htons(sizeof *oana);
	oana->nw_addr = htonl(PIP);

   	oao = (struct ofp_action_output *)&ofm->actions[1];
   	oao->type = htons(OFPAT_OUTPUT);
   	oao->len = htons(sizeof *oao);
   	oao->port = htons(port);

   	queue_tx(sw, rconn, out);
}

void modifyFlowEntryPIP(struct rconn* rconn, struct lswitch* sw, uint32_t PIP, uint16_t port)
{
	struct ofp_flow_mod *ofm;
	struct ofp_action_output *oao;
	ssize_t size;
	uint32_t wildcard;
	struct ofpbuf *out;

   	VLOG_DBG("Inserting modify L3 entry in the edge switch PIP: %x port: %d\n", PIP, port);

    wildcard = OFPFW_IN_PORT |
                OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                OFPFW_DL_DST |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST;

	size = sizeof *ofm + sizeof *oao;
   	out = ofpbuf_new(size);
   	ofm = ofpbuf_put_zeros(out, size);

   	ofm->header.version = OFP_VERSION;
   	ofm->header.type = OFPT_FLOW_MOD;
   	ofm->header.length = htons(size);

   	ofm->match.dl_type = htons(0x0800);
    ofm->match.nw_dst = (PIP);


   	ofm->command = htons(OFPFC_MODIFY);
   	ofm->buffer_id = htonl(-1);
   	ofm->out_port = OFPP_NONE;

   	oao = (struct ofp_action_output *)&ofm->actions[0];
   	oao->type = htons(OFPAT_OUTPUT);
   	oao->len = htons(sizeof *oao);
   	oao->port = htons(port);

   	queue_tx(sw, rconn, out);
}

void deleteFlowEntries(struct rconn* rconn, struct lswitch* sw, uint8_t *dstPMAC)
{
	struct ofp_flow_mod *ofm;
	ssize_t size;
	uint32_t wildcard;
	struct ofpbuf *out;

	VLOG_WARN("DELETING forwarding prefix in the switch\n");
	VLOG_DBG("%x:%x:%x:%x:%x:%x ", dstPMAC[0],dstPMAC[1],dstPMAC[2],dstPMAC[3],dstPMAC[4],dstPMAC[5]);

    wildcard = OFPFW_IN_PORT |
                OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST ;   
	size = sizeof *ofm;
   	out = ofpbuf_new(size);
   	ofm = ofpbuf_put_zeros(out, size);

   	ofm->header.version = OFP_VERSION;
   	ofm->header.type = OFPT_FLOW_MOD;
   	ofm->header.length = htons(size);

   	ofm->match.dl_type = htons(0x0800);
   	ofm->match.wildcards = htonl(wildcard);
   	memcpy(ofm->match.dl_dst, dstPMAC, 6);


   	ofm->command = htons(OFPFC_DELETE);
   	ofm->buffer_id = htonl(-1);
   	ofm->out_port = OFPP_NONE;

   	queue_tx(sw, rconn, out);
}

void deleteFlowEntriesPIP(struct rconn* rconn, struct lswitch* sw, uint32_t PIP)
{
	struct ofp_flow_mod *ofm;
	ssize_t size;
	uint32_t wildcard;
	struct ofpbuf *out;
	location loc = extractLocationFromIP(ntohl(PIP));

	VLOG_DBG("DELETING forwarding prefix in the switch\n");

	VLOG_DBG("Entry Deletied corresonding to %.8x", PIP);
			
	VLOG_DBG("&&&&&& POD=%d, Position=%d, port=%d", loc.pod, loc.position, loc.port);

        wildcard = OFPFW_IN_PORT |
                OFPFW_DL_VLAN |
                OFPFW_DL_SRC |
                OFPFW_DL_DST |
                OFPFW_DL_TYPE |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST |
		OFPFW_NW_SRC_ALL ;   

	size = sizeof *ofm;
   	out = ofpbuf_new(size);
   	ofm = ofpbuf_put_zeros(out, size);

   	ofm->header.version = OFP_VERSION;
   	ofm->header.type = OFPT_FLOW_MOD;
   	ofm->header.length = htons(size);

   	ofm->match.dl_type = htons(0x0800);
    ofm->match.nw_dst = PIP;

   	ofm->command = htons(OFPFC_DELETE);
   	ofm->buffer_id = htonl(-1);
   	ofm->out_port = OFPP_NONE;

   	queue_tx(sw, rconn, out);
}

void insert_modify_L2_entry(struct rconn* rconn, struct lswitch* sw, uint8_t* dstAMAC, uint8_t *dstPMAC, uint16_t port)
{
        struct ofp_flow_mod *ofm;
        struct ofp_action_output *oao;
        struct ofp_action_dl_addr *oada;
        ssize_t size;
        uint32_t wildcard;
        struct ofpbuf *out;

        VLOG_DBG("[%s] started", __PRETTY_FUNCTION__);


        wildcard = OFPFW_IN_PORT |
                OFPFW_DL_SRC |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST |
                OFPFW_DL_VLAN |
                OFPFW_NW_SRC_ALL |
                OFPFW_NW_DST_ALL ;


    size = sizeof *ofm + sizeof *oao + sizeof *oada;
    out = ofpbuf_new(size);
    ofm = ofpbuf_put_zeros(out, size);

    ofm->header.version = OFP_VERSION;
    ofm->header.type = OFPT_FLOW_MOD;
    ofm->header.length = htons(size);

    ofm->match.dl_type = htons(0x0800);
    ofm->match.wildcards = htonl(wildcard);


    memcpy(ofm->match.dl_dst, dstPMAC, 6);


    ofm->command = htons(OFPFC_ADD);
    ofm->idle_timeout = htons(getIdleTimeout());
    ofm->hard_timeout = htons(getHardTimeout());
    ofm->buffer_id = htonl(-1);

    oada = (struct ofp_action_dl_addr *)&ofm->actions[0];
    oada->type = htons(OFPAT_SET_DL_DST);
    oada->len = htons(sizeof *oada);
    memcpy(oada->dl_addr, dstAMAC, ETH_ADDR_LEN);

    oao = (struct ofp_action_output *)&ofm->actions[2];
    oao->type = htons(OFPAT_OUTPUT);
    oao->len = htons(sizeof *oao);
    oao->port = htons(port);

	VLOG_DBG("Done with inserting modify L2 entry");
    VLOG_DBG("[%s] ended", __PRETTY_FUNCTION__);
    queue_tx(sw, rconn, out);

}

void insert_L3_entry(struct rconn* rconn, struct lswitch* sw, uint32_t dstPIP, uint16_t port)
{
    struct ofp_flow_mod *ofm;
	struct ofp_action_output *oao;
    ssize_t size;
    uint32_t wildcard;
    struct ofpbuf *out;

    VLOG_DBG("[%s] started", __PRETTY_FUNCTION__);


    wildcard = OFPFW_IN_PORT |
                OFPFW_DL_SRC |
                OFPFW_DL_DST |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST |
                OFPFW_DL_VLAN |
                OFPFW_NW_SRC_ALL ;
                //OFPFW_NW_DST_ALL ;


    size = sizeof *ofm + sizeof *oao;
    out = ofpbuf_new(size);
    ofm = ofpbuf_put_zeros(out, size);

    ofm->header.version = OFP_VERSION;
    ofm->header.type = OFPT_FLOW_MOD;
    ofm->header.length = htons(size);

    ofm->match.dl_type = htons(0x0800);
    ofm->match.wildcards = htonl(wildcard);


    //memcpy(ofm->match.dl_dst, dstPMAC, 6);
    ofm->match.nw_dst = htonl(dstPIP);
    //print_entry(ofm->match.dl_dst, port);

    ofm->command = htons(OFPFC_ADD);
    ofm->idle_timeout = htons(getIdleTimeout());
    ofm->hard_timeout = htons(getHardTimeout());
    ofm->buffer_id = htonl(-1);


    oao = (struct ofp_action_output *)&ofm->actions[0];
    oao->type = htons(OFPAT_OUTPUT);
    oao->len = htons(sizeof *oao);
    oao->port = htons(port);

    queue_tx(sw, rconn, out);

    VLOG_DBG("[%s] ended", __PRETTY_FUNCTION__);
}


void insert_modify_L3_entry(struct rconn* rconn, struct lswitch* sw, uint32_t dstAIP, uint32_t dstPIP , uint16_t port)
{
    struct ofp_flow_mod *ofm;
    struct ofp_action_output *oao;
    struct ofp_action_nw_addr *oana;

    ssize_t size;
    uint32_t wildcard;
    struct ofpbuf *out;
    VLOG_DBG("[%s] started", __PRETTY_FUNCTION__);

    VLOG_DBG("Inserting modify L3 entry in the edge switch PIP: %x AIP: %x port: %d\n", dstAIP, dstPIP, port);
    wildcard = OFPFW_IN_PORT |
                OFPFW_DL_SRC |
                OFPFW_DL_DST |
                OFPFW_NW_PROTO |
                OFPFW_TP_SRC |
                OFPFW_TP_DST |
                OFPFW_DL_VLAN |
                OFPFW_NW_SRC_ALL ;
           //     OFPFW_NW_DST_ALL ;

    size = sizeof *ofm + sizeof *oao + sizeof *oana;
    out = ofpbuf_new(size);
    ofm = ofpbuf_put_zeros(out, size);

    ofm->header.version = OFP_VERSION;
    ofm->header.type = OFPT_FLOW_MOD;
    ofm->header.length = htons(size);

    ofm->match.dl_type = htons(0x0800);
    ofm->match.wildcards = htonl(wildcard);


    ofm->match.nw_dst = htonl(dstAIP);

    ofm->command = htons(OFPFC_ADD);
    ofm->idle_timeout = htons(getIdleTimeout());
    ofm->hard_timeout = htons(getHardTimeout());
    ofm->buffer_id = htonl(-1);

    oana = (struct ofp_action_nw_addr *)&ofm->actions[0];
    oana->type = htons(OFPAT_SET_NW_DST);
    oana->len = htons(sizeof *oana);
	oana->nw_addr = htonl(dstPIP);

    oao = (struct ofp_action_output *)&ofm->actions[1];
    oao->type = htons(OFPAT_OUTPUT);
    oao->len = htons(sizeof *oao);
    oao->port = htons(port);

    queue_tx(sw, rconn, out);

    VLOG_DBG("[%s] ended", __PRETTY_FUNCTION__);
}
